import java.util.ArrayList;
import java.util.Random;

/**
 * this class running the genetic algorithm-
 * every generation deduct the networks on random Examples Input
 */
public class GeneticAlgorithm {

	private SortingNetwork[] population;
	private static Random rand = new Random();
	private FitnessCalculator fitnessFunction = new FitnessCalculator();
	private PopulationBuilder cm =new  PopulationBuilder();
	private int[] fitness ;
	private char state ;
	
	OutputGraphs output=new OutputGraphs();
	
	
	/**
	 * this functition run the genetic algorithm and return the best network according to the state:
	 * the state can be Constants.EVOVLING_INPUTS and then the examples are changes every iteration 
	 */
	public SortingNetwork searchForSoultion(char state) {
		this.state =state; 
		population = new SortingNetwork[Constants.POPULATION_SIZE];
		cm= new PopulationBuilder();
		
		
		buildInitPopulation();
		for (int i = 0; i < Constants.NUMBER_OF_GENERATIONS; i++) {
			evolveNextGenration(i);
		}
		SortingNetwork best = population[extractMax()];
		printMax(best);
		return best;
	}


	/**
	 * the function run over fitness' list and find the max one
	 * @return the index of the max networks' fitness in the population
	 */
	private int extractMax() {
		int max = -1;
		int idx = -1;
		for (int i = 0; i < this.fitness.length; i++) {
			if (this.fitness[i]>max){
				max=this.fitness[i];
				idx=i;
				
			}
		}

//		printMax(population[idx]);
		return idx;
	}


	/**
	 * print the best network with the input examples
	 * @param sortingNetwork- get the best network
	 */
	private void printMax(SortingNetwork sortingNetwork) {
		int [][] inputEx= fitnessFunction.getInputExamples();
		int [][] sortedEx= fitnessFunction.getSortedInputExamples();
		int cn=0;
		for (int j = 0; j < inputEx.length; j++) {

			
			int a[]=sortingNetwork.sortInput(inputEx[j]);
				//int a[]=sortingNetwork.sortInputRightToLeft(inputEx[j]);
				
				for (int j2 = 0; j2 < a.length; j2++) {
					System.out.print(a[j2]+" ");
					if (a[j2]==sortedEx[j][j2])
						cn++;
				}
				System.out.println();
				a=sortedEx[j];
				for (int j2 = 0; j2 < a.length; j2++) {
					System.out.print(a[j2]+" ");
				}
				System.out.println();
				System.out.println("------------------");
			}
			System.out.println(sortingNetwork.getNetwork().size());
			System.out.println(cn/(double)Constants.EXAMPLES_NUM);
		
	}


	/**
	 * build the start population of the networks- the first generation
	 */
	private void buildInitPopulation() {
		for (int i = 0; i < Constants.POPULATION_SIZE; i++) {
			population[i]=(createRandomNetwork());
		}
	}

	/**
	 * the function create random network by- choosing randomly the size (number of components)
	 * and choosing randomly the edges
	 * @return a random network
	 */
	private SortingNetwork createRandomNetwork() { 
		int networkSize = rand.nextInt(Constants.NETWORK_LIMIT)+2;
		ArrayList<Integer> array=  new ArrayList<Integer>();
		for (int i = 0; i < networkSize; i++) {
			array.add(rand.nextInt(SortingNetwork.edgesCode.length));
		}
		return new SortingNetwork(array);
	}


	/**
	 * by getting the parameter state determine if make generation of
	 * genetic algorithm on the examples  
	 * create the next generation by making all the genetic operations
	 * @param iteration
	 */
	private void evolveNextGenration(int iteration) {
		
		
		fitness =new int[Constants.POPULATION_SIZE];
		for (int i = 0; i < population.length; i++) {
			fitness[i]=fitnessFunction.calculateFitness(population[i]);
			System.out.print(fitness[i]+" ");
		}
		output.outToFile(fitnessFunction,population,fitness,fitness[extractMax()]);
		System.out.println("END");
		
		SortingNetwork [] newPopulation = new  SortingNetwork[Constants.POPULATION_SIZE]; 
		
		cm.buildNewPopulation(population,newPopulation,fitness);
		if (state==Constants.EVOVLING_INPUTS)
			fitnessFunction.evolveInputsToNextGenration(iteration,population,fitness);
		this.population= newPopulation;
		
		
		
		

	}









}